package com.exchange.used.service.impl;

import com.exchange.used.config.constant.K;
import com.exchange.used.config.query.Condition;
import com.exchange.used.config.query.Order;
import com.exchange.used.config.query.Where;
import com.exchange.used.config.utils.ContextUtils;
import com.exchange.used.config.utils.FastUtils;
import com.exchange.used.config.utils.FormatUtil;
import com.exchange.used.config.utils.SuperSqlBuilder;
import com.exchange.used.entity.Role;
import com.exchange.used.entity.User;
import com.exchange.used.service.CommonServiceI;
import com.exchange.used.service.RestfulServiceI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.Id;
import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;

@Service
public class CommonService implements CommonServiceI {

    @Autowired
    JdbcTemplate jdbcTemplate;

    private Class<?> clazz;
    private JpaRepositoryImplementation repo;
    private Class idType;


    @Override
    @Transactional
    public boolean executeSaveOrUpdate(String dataTable, List<Map<String, Object>> beans) {
        if (init(dataTable)) {
            Object entity;
            Field field;
            try {
                for (Map<String, Object> bean : beans) {
                    entity = this.clazz.newInstance();
                    for (Map.Entry<String, Object> entry : bean.entrySet()) {
                        try {
                            field = clazz.getDeclaredField(entry.getKey());
                            field.setAccessible(true);
                            field.set(entity, FormatUtil.getStdReflectValue(field, entry.getValue().toString()));
                        } catch (Exception e) {
                            System.out.println("未找到相应字段：" + entry.getKey());
                        }
                    }
                    this.repo.save(entity);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    @Override
    public boolean executeDelete(String dataTable, List<Object> ids) {
        if (init(dataTable)) {
            try {
                for (Object id : ids) {
                    repo.deleteById(this.idType.cast(id));
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    @Override
    public List executeQuery(Condition condition) {
        if (init(condition.getFirstTable())) {
            Sort sort = getSort(condition);
            PageRequest pageReq = getPageRequest(condition, sort);
            Specification specification = getSpecification(condition);

            if (pageReq != null) {
                return repo.findAll(specification, pageReq).getContent();
            } else if (sort != null) {
                return repo.findAll(specification, sort);
            } else {
                return repo.findAll(specification);
            }
        }
        return null;
    }


//    @Override
//    public List executeQuery(Condition condition) {
//        if (init(condition.getFirstTable())) {
//            Sort sort = new Sort(Sort.Direction.DESC, "userid");
//            PageRequest pageReq = PageRequest.of(0, 2, sort);
//
//            return repo.findAll((Specification<?>) (root, query, criteriaBuilder) -> {
//                //1.混合条件查询
//                Path<String> exp1 = root.get("username");
//                Path<Integer> exp2 = root.get("age");
//
//                //2.多表查询
//                Join<?, ?> join = root.join("role", JoinType.LEFT);
//                Path<String> exp4 = join.get("rolename");
//                return criteriaBuilder.and(
//                        criteriaBuilder.like(exp1, "%a%")
//                        , criteriaBuilder.lessThan(exp2, 24)
//                        , criteriaBuilder.like(exp4, "%用%")
//                );
//            }, pageReq).getContent();
//        }
//        return null;
//    }

    @Override
    public Map<String, Object> getCount(Condition condition) {
        SuperSqlBuilder superSqlBuilder = new SuperSqlBuilder(condition);
        String sql = superSqlBuilder.getCount();
        return jdbcTemplate.queryForList(sql).get(0);
    }

    private Sort getSort(Condition condition){
        if (condition.getOrder() != null) {
            return null;
        }
        Order order = condition.getOrder();
        String table = order.getField().split("\\.")[0];
        String field = order.getField().split("\\.")[1];
        Sort sort;
        if (order.isMode()) {
            sort = new Sort(Sort.Direction.ASC, field);
        } else {
            sort = new Sort(Sort.Direction.DESC, field);
        }
        return sort;
    }

    private PageRequest getPageRequest(Condition condition, Sort sort){
        if (condition.getLimit() == null || condition.getPage() == null) {
            return null;
        }
        PageRequest pageRequest;
        if (sort != null) {
            pageRequest = PageRequest.of(condition.getPage(), condition.getLimit(), sort);
        } else {
            pageRequest = PageRequest.of(condition.getPage(), condition.getLimit());
        }
        return pageRequest;
    }

    private Specification getSpecification(Condition condition){
        return (root, query, criteriaBuilder) -> {
            Map<String, Join<?, ?>> joinMap = new HashMap<>();
            Join<?, ?> join;
            if (condition.getRelations() != null) {
                for (Map.Entry<String, String> entry : condition.getRelations().entrySet()) {
                    String rightTableName = entry.getKey().split("\\.")[0];
                    String joinTableName = entry.getValue().split("\\.")[0];
                    if (joinMap.get(rightTableName) != null) {
                        join = joinMap.get(rightTableName).join(joinTableName, JoinType.LEFT);
                    } else {
                        join = root.join(joinTableName, JoinType.LEFT);
                    }
                    joinMap.put(joinTableName, join);
                }
            }
            if (condition.getWheres() != null) {
                List<Predicate> predicateList = new ArrayList<>();
                for (Where where : condition.getWheres()) {
                    String value = where.getValue();
                    //若条件值为空(包括空格字符)，则不作为检索条件
                    if (value != null && !value.trim().isEmpty()) {
                        String table = where.getField().split("\\.")[0];
                        String field = where.getField().split("\\.")[1];
                        int mode;
                        try {
                            mode = Integer.parseInt(where.getMode());
                            Expression expression;
                            if (joinMap.get(table) != null) {
                                expression = joinMap.get(table).get(field);
                            } else {
                                expression = root.get(field);
                            }
                            if (mode == -2) {
                                predicateList.add(criteriaBuilder.isNotNull(expression));
                            } else if (mode == -1) {
                                predicateList.add(criteriaBuilder.isNull(expression));
                            } else if (mode == 0) {
                                predicateList.add(criteriaBuilder.notEqual(expression, value.trim()));
                            } else if (mode == 1) {
                                predicateList.add(criteriaBuilder.equal(expression, value.trim()));
                            } else if (mode == 2) {
                                predicateList.add(criteriaBuilder.like(expression, "%" + value.trim() + "%"));
                            } else {
                                throw new RuntimeException("检索条件状态值异常");
                            }
                        } catch (NumberFormatException e) {
                            System.out.println(FastUtils.hightLightLog(e.getMessage()));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                Predicate[] predicates = new Predicate[0];
                return criteriaBuilder.and(predicateList.toArray(predicates));
            }
            return null;
        };
    }

/*
    private Object map2Entity(Map<String, Object> bean){
        Object entity = null;
        Field field;
        for (Map.Entry<String, Object> entry : bean.entrySet()) {
            try {
                entity = this.clazz.newInstance();
                field = clazz.getDeclaredField(entry.getKey());
                if(field != null && entry.getValue() != null) {
                    field.setAccessible(true);
                    field.set(entity, this.idType.cast(entry.getValue()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return entity;
    }

    private List<Object> listMap2ListEntity(List<Map<String, Object>> beans){
        List<Object> list = new ArrayList<>();
        for(Map<String, Object> bean : beans){
            Object entity = map2Entity(bean);
            list.add(entity);
        }
        return list;
    }
*/


    /**
     * 初始化方法，初始化实体类，对应JPA，主键类型，并检查初始化是否成功
     *
     * @param dataTable 实体名
     * @return 初始化是否成功
     */
    private boolean init(String dataTable) {
        try {
            ApplicationContext ctx = ContextUtils.getContext();
            String fullPackName = getFullPackName(dataTable);
            this.repo = (JpaRepositoryImplementation) ctx.getBean(getRepoName(dataTable));
            this.clazz = Class.forName(fullPackName);
            this.idType = getIdType(this.clazz);
            return check();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private String getRepoName(String dataTable) {
        if (StringUtils.isEmpty(dataTable)) {
            throw new RuntimeException("dataTable can't be null or empty.");
        }
        return dataTable + "Repository";
    }

    private String getFullPackName(String dataTable) {
        if (StringUtils.isEmpty(dataTable)) {
            throw new RuntimeException("dataTable can't be null or empty.");
        }
        return K.ENTITY_PATH + "." + StringUtils.capitalize(dataTable);
    }

    private Class getIdType(Class clazz) {
        if (clazz == null) {
            throw new RuntimeException("Can not find the entity.");
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Id.class) != null) {
                return field.getType();
            }
        }
        throw new RuntimeException("The entity [" + this.clazz.getName() + "]  has no primary key.");
    }

    private boolean check() {
        return this.clazz != null && this.repo != null && this.idType != null;
    }
}
